package cn.klaw.selenium.driver;

import cn.klaw.selenium.properties.SeleniumProperties;
import cn.klaw.selenium.SeleniumHelper;
import org.openqa.selenium.*;
import org.openqa.selenium.remote.AbstractDriverOptions;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.remote.service.DriverService;

import java.time.Duration;
import java.util.List;
import java.util.Map;

public interface SeleniumBrowser<D extends RemoteWebDriver, S extends DriverService, O extends AbstractDriverOptions<?>> {

    D getDriver();

    S getDriverService();

    O getDriverOptions();

    /**
     * 关闭驱动和浏览器
     */
    void close();

    /**
     * 设置页面加载超时时间
     */
    default void setPageLoadTimeout(Duration duration) {
        getDriver().manage().timeouts().pageLoadTimeout(duration);
    }

    /**
     * 设置查找元素超时时间
     */
    default void setImplicaitlyWaitTimeout(Duration duration) {
        getDriver().manage().timeouts().implicitlyWait(duration);
    }

    /**
     * 设置执行脚本超时时间
     */
    default void setScriptTimeout(Duration duration) {
        getDriver().manage().timeouts().scriptTimeout(duration);
    }

    /**
     * 寻找元素, 若不存在抛出未找到元素异常
     * 如需判断元素是否存在可调用{@link #findElements} 进行判空处理, 为空获取默认超时配置 {@link SeleniumProperties#getImplicaitlyWaitTimeout()}
     *
     * @param locator     查找条件
     * @param findTimeout 最长等待时间
     */
    default WebElement findElement(By locator, Duration findTimeout) {
        findTimeout = findTimeout == null ? SeleniumHelper.getSeleniumProperties().getImplicaitlyWaitTimeout() : findTimeout;
        setImplicaitlyWaitTimeout(findTimeout);
        return getDriver().findElement(locator);
    }

    /**
     * 寻找元素, 未找到元素返回空
     *
     * @param locator     查找条件
     * @param findTimeout 最长等待时间, 为空获取默认超时配置 {@link SeleniumProperties#getImplicaitlyWaitTimeout()}
     */
    default List<WebElement> findElements(By locator, Duration findTimeout) {
        findTimeout = findTimeout == null ? SeleniumHelper.getSeleniumProperties().getImplicaitlyWaitTimeout() : findTimeout;
        setImplicaitlyWaitTimeout(findTimeout);
        return getDriver().findElements(locator);
    }

    /**
     * 加载页面
     * @param url 网页地址
     * @param loadTimeout 加载超时时间, 为空获取默认超时配置 {@link SeleniumProperties#getPageLoadTimeout()}
     * @param newTab 新窗口打开链接
     */
    default void loadPage(String url, Duration loadTimeout, WindowType newTab) {
        if (newTab != null) {
            getDriver().switchTo().newWindow(newTab);
        }
        loadTimeout = loadTimeout == null ? SeleniumHelper.getSeleniumProperties().getPageLoadTimeout() : loadTimeout;
        setPageLoadTimeout(loadTimeout);
        getDriver().get(url);
    }

    /**
     * 执行JS脚本
     * @param script 脚本内容
     * @param scriptTimeout 脚本执行超时时间, 为空获取默认超时配置 {@link SeleniumProperties#getScriptTimeout()}
     * @param args 脚本内容参数
     */
    default Object executeScript(String script, Duration scriptTimeout, Object... args){
        scriptTimeout = scriptTimeout == null ? SeleniumHelper.getSeleniumProperties().getScriptTimeout() : scriptTimeout;
        setScriptTimeout(scriptTimeout);
        return getDriver().executeScript(script, args);
    }

    /**
     * 获取最大的页面大小
     */
    default Dimension getDocumentSize() {
        Map<String, Long> result = (Map<String, Long>) getDriver().executeScript("return {'w':Math.max(document.body.scrollWidth, document.body.offsetWidth, document.documentElement.clientWidth, document.documentElement.scrollWidth, document.documentElement.offsetWidth), 'h':Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight)}");
        return new Dimension(Math.toIntExact(result.get("w")), Math.toIntExact(result.get("h")));
    }

    /**
     * 窗口最大化, 截取全部网页内容
     */
    default byte[] screenShotByWindow() {
        getDriver().manage().window().setSize(getDocumentSize());
        return getDriver().getScreenshotAs(OutputType.BYTES);
    }


    /**
     * 窗口最大化, 截取指定元素
     */
    default byte[] screenShotByElement(WebElement element) {
        getDriver().manage().window().setSize(getDocumentSize());
        return element.getScreenshotAs(OutputType.BYTES);
    }

    /**
     * 可能存在懒加载, 滚动页面到底部进行触发, 每次滚动完后延时指定时间等待触发
     * @param scrollDelay 每次滚动完后延时， 等待触发
     */
    default void scrollToBottom(Duration scrollDelay) {
        Dimension documentSize = getDocumentSize();
        long height = documentSize.getHeight();
        //这里需要模拟滑动，有些是滑动的时候才加载的
        long temp_height = 0;
        while (true) {
            temp_height += 500;
            if (temp_height >= height) {
                break;
            }
            getDriver().executeScript("window.scrollBy(0,500)");
            try {
                Thread.sleep(scrollDelay.toMillis());
            } catch (Exception e) {
            }
        }
    }


}
